#include <stdio.h>

#include "esp_tts.h"
#include "esp_tts_voice_template.h"
#include "esp_partition.h"
#include "driver/i2s.h"

#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wn_iface.h"
#include "esp_wn_models.h"
#include "esp_afe_sr_models.h"
#include "esp_mn_iface.h"
#include "esp_mn_models.h"
#include "model_path.h"
#include "string.h"
#include "esp_mn_speech_commands.h"
#include "esp_process_sdkconfig.h"

#define I2S_SAMPLE_RATE     (16000)  // 采样率
#define I2S_BITS_PER_SAMPLE (16)     // 位宽（16/24/32）
#define I2S_NUM_CHANNELS    (1)      // 声道数（1/2）

void i2s_speaker_init() {
    i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX,  // 主模式 + 发送
        .sample_rate = I2S_SAMPLE_RATE,
        .bits_per_sample = I2S_BITS_PER_SAMPLE,
        .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,  // 单声道
        .communication_format = I2S_COMM_FORMAT_STAND_I2S,
        .dma_buf_count = 8,      // DMA 缓冲区数量
        .dma_buf_len = 1024,     // 每个缓冲区长度（样本数）
        .use_apll = false,       // 是否使用 APLL 时钟（提高精度）
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,  // 中断优先级
        .tx_desc_auto_clear = true //自动清除TX描述符防止溢出
    };


    i2s_pin_config_t pin_config = {
        .bck_io_num = 39,    // BCLK
        .ws_io_num = 40,     // LRCLK
        .data_out_num = 38,  // DATA
        .data_in_num = I2S_PIN_NO_CHANGE  // 不用于输入
    };

    // 安装 I2S 驱动
    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    i2s_set_pin(I2S_NUM_0, &pin_config);
    i2s_set_sample_rates(I2S_NUM_0, I2S_SAMPLE_RATE);  // 设置采样率
}

void play_audio(const uint8_t *pcm_data, size_t len) {
    size_t bytes_written = 0;
    i2s_write(I2S_NUM_0, pcm_data, len, &bytes_written, portMAX_DELAY);
}

void i2s_microphone_init() {
    i2s_config_t i2s_config = {
       .mode = I2S_MODE_MASTER | I2S_MODE_RX,  // 主模式 + 接收
       .sample_rate = I2S_SAMPLE_RATE,
       .bits_per_sample = I2S_BITS_PER_SAMPLE,
       .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,  // 单声道
       .communication_format = I2S_COMM_FORMAT_STAND_I2S,
       .dma_buf_count = 8,      // DMA 缓冲区数量
       .dma_buf_len = 1024,     // 每个缓冲区长度（样本数）
       .use_apll = false,       // 是否使用 APLL 时钟（提高精度）
       .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1  // 中断优先级
    };

    i2s_pin_config_t pin_config = {
        .bck_io_num = 41,    // BCLK
        .ws_io_num = 42,     // LRCLK
        .data_out_num = I2S_PIN_NO_CHANGE,  // DATA
        .data_in_num = 2  // 不用于输入
    };
    // 安装 I2S 驱动
    esp_err_t err = i2s_driver_install(I2S_NUM_1, &i2s_config, 0, NULL);
    if (err != ESP_OK)
    {
        /* code */
        printf("i2s driver install error\n");
    }
    

    err = i2s_set_pin(I2S_NUM_1, &pin_config);
    if (err != ESP_OK)
    {
        /* code */
        printf("i2s set pin error\n");
    }
    printf("microphone init done\n");
}

esp_tts_handle_t *tts_handle;
void tts_handle_init() {
    const esp_partition_t* part=esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, "voice_data");
    if (part==0) printf("Couldn't find voice data partition!\n");
    esp_partition_mmap_handle_t  mmap;
    uint16_t* voicedata;
    esp_err_t err=esp_partition_mmap(part, 0, part->size, ESP_PARTITION_MMAP_DATA, &voicedata, &mmap);

    if (err != ESP_OK) {
        printf("Couldn't map voice data partition!\n"); 
        return;
    }
    esp_tts_voice_t *voice=esp_tts_voice_set_init(&esp_tts_voice_template, (int16_t*)voicedata); 
    tts_handle=esp_tts_create(voice);
    // 2. parse text and synthesis wave data
    // char *text="测试,测试,,A,B,C,D,E,,1,2,3,4,5,,你好,,今天,天气,不错,,你叫什么名字,,你,几岁,,你,喜欢,什么,电影,,你,喜欢,什么,歌曲,,本示例,主要用于展示乐鑫中文语音合成库的使用方法,示例可通过URAT串口输入中文文本,回车后播放合成的语音数据...";
    // if (esp_tts_parse_chinese(tts_handle, text)) {  // parse text into pinyin list
    //     int len[1]={0};
    //     do {
    //         short *data=esp_tts_stream_play(tts_handle, len, 3); // streaming synthesis
    //         play_audio((uint8_t*)data, len[0]*2); // play audio data
            
    //     } while(len[0]>0);
    //     vTaskDelay(300);
    //     i2s_zero_dma_buffer(I2S_NUM_0);
    // }

    // speek_chinese(text,tts_handle);

}


void speek_chinese(char *text) {
    if (esp_tts_parse_chinese(tts_handle, text)) {  // parse text into pinyin list
        int len[1]={0};
        do {
            short *data=esp_tts_stream_play(tts_handle, len, 3); // streaming synthesis
            play_audio((uint8_t*)data, len[0]*2); // play audio data
            
        } while(len[0]>0);
        // vTaskDelay(300);
        // i2s_zero_dma_buffer(I2S_NUM_0);
    }
}



int detect_flag = 0;
static esp_afe_sr_iface_t *afe_handle = NULL;
static volatile int task_flag = 0;
srmodel_list_t *models;
static volatile int wakeup_flag = 0;
static volatile int sys_speaking_flag = 0;

void sys_speek_chinese(char *text) {
    if (esp_tts_parse_chinese(tts_handle, text)) {  // parse text into pinyin list
        int len[1]={0};
        sys_speaking_flag = 1;
        vTaskDelay(10 / portTICK_PERIOD_MS);
        do {
            short *data=esp_tts_stream_play(tts_handle, len, 1); // streaming synthesis
            play_audio((uint8_t*)data, len[0]*2); // play audio data
            
        } while(len[0]>0);
        sys_speaking_flag = 0;
        // vTaskDelay(300);
        // i2s_zero_dma_buffer(I2S_NUM_0);
    }
}

void feed_Task(void *arg)
{
    printf("------------feed start------------\n");
    esp_afe_sr_data_t *afe_data = arg;
    int audio_chunksize = afe_handle->get_feed_chunksize(afe_data);
    int nch = afe_handle->get_feed_channel_num(afe_data);

    printf("chunksize %d, channel %d\n", audio_chunksize, nch);
    
    size_t buff_size = audio_chunksize * sizeof(int16_t) * nch;

    int16_t *i2s_buff = malloc(buff_size);
    // assert(i2s_buff);

    while (task_flag) {
        vTaskDelay(10 / portTICK_PERIOD_MS);
        if(sys_speaking_flag == 0){
        
            // esp_get_feed_data(true, i2s_buff, audio_chunksize * sizeof(int16_t) * feed_channel);
            size_t bytes_read = 0;
            i2s_read(I2S_NUM_1, (void*)i2s_buff, buff_size, &bytes_read, portMAX_DELAY);

            // printf("feed data size: %d\n", bytes_read);
            // size_t bytes_written = 0;
            // i2s_write(I2S_NUM_0, i2s_buff, buff_size, &bytes_written, portMAX_DELAY);
            
            afe_handle->feed(afe_data, i2s_buff);
        }else{
            // printf("sys speaking escape feed\n");
        }
        

    }
    if (i2s_buff) {
        free(i2s_buff);
        i2s_buff = NULL;
    }
    vTaskDelete(NULL);


}

void detect_Task(void *arg)
{
    esp_afe_sr_data_t *afe_data = arg;
    int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data);
    int16_t *buff = malloc(afe_chunksize * sizeof(int16_t));
    assert(buff);
    printf("------------detect start------------\n");



    char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_CHINESE);
    printf("multinet:%s\n", mn_name);
    const esp_mn_iface_t *multinet = esp_mn_handle_from_name(mn_name);
    model_iface_data_t *model_data = multinet->create(mn_name, 6000);
    esp_mn_commands_update_from_sdkconfig(multinet, model_data);
    esp_mn_active_commands_print();
    
    int mu_chunksize = multinet->get_samp_chunksize(model_data);
    assert(mu_chunksize == afe_chunksize);

    while (task_flag) {
        vTaskDelay(10 / portTICK_PERIOD_MS);
        afe_fetch_result_t* res = afe_handle->fetch(afe_data); 
        // afe_fetch_result_t* res = afe_handle->fetch_with_delay(afe_data, 100 / portTICK_PERIOD_MS);

        if (!res || res->ret_value == ESP_FAIL) {
            printf("fetch error!\n");
            break;
        }
        // printf("vad state: %d wakeup state: %d\n", res->vad_state, res->wakeup_state);

        if (res->wakeup_state == WAKENET_DETECTED) {
            // afe_handle->reset_buffer(afe_data);
            printf("wakeword detected\n");
	        printf("model index:%d, word index:%d\n", res->wakenet_model_index, res->wake_word_index);
            printf("-----------LISTENING-----------\n");
            
            sys_speek_chinese("你好啊");

            wakeup_flag = 1;
            multinet->clean(model_data);
        }

        
        if (wakeup_flag == 1) {


            esp_mn_state_t mn_state = multinet->detect(model_data, res->data);

            // printf("multinet state: %d\n", mn_state);

            if (mn_state == ESP_MN_STATE_DETECTING) {
                continue;
            }

            if (mn_state == ESP_MN_STATE_DETECTED) {
                esp_mn_results_t *mn_result = multinet->get_results(model_data);
                for (int i = 0; i < mn_result->num; i++) {
                    printf("TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n", 
                    i+1, mn_result->command_id[i], mn_result->phrase_id[i], mn_result->string, mn_result->prob[i]);
                }
                printf("\n-----------listening-----------\n");
                // speek_chinese("知道了");
                if(strcmp(mn_result->string, "dakaikongtiao") == 0){
                    sys_speek_chinese("空调已经打开");
                }else if (strcmp(mn_result->string, "ceshiceshi") == 0)
                {
                    sys_speek_chinese("测试命令调用成功");
                }else{
                    sys_speek_chinese("不知道你说什么");
                }
                
            }

            if (mn_state == ESP_MN_STATE_TIMEOUT) {
                esp_mn_results_t *mn_result = multinet->get_results(model_data);
                printf("timeout, string:%s\n", mn_result->string);
                afe_handle->enable_wakenet(afe_data);
                wakeup_flag = 0;
                printf("\n-----------awaits to be waken up-----------\n");
                sys_speek_chinese("还有什么可以帮到你吗");
                continue;
            }
        }





    }

    if (model_data) {
        multinet->destroy(model_data);
        model_data = NULL;
    }

    if (buff) {
        free(buff);
        buff = NULL;
    }
    vTaskDelete(NULL);
}


void sr_init() {
    models = esp_srmodel_init("model");
    if (models) {
        for (int i=0; i<models->num; i++) {
            if (strstr(models->model_name[i], ESP_WN_PREFIX) != NULL) {
                printf("wakenet model in flash: %s\n", models->model_name[i]);
            }
        }
    }

    // afe_config_t *afe_config = afe_config_init(esp_get_input_format(), models, AFE_TYPE_SR, AFE_MODE_LOW_COST);
    // afe_config_t *afe_config = afe_config_init(esp_get_input_format(), models, AFE_TYPE_SR, AFE_MODE_HIGH_PERF);

    
    afe_config_t *afe_config = afe_config_init("M", models, AFE_TYPE_SR, AFE_MODE_HIGH_PERF);
    
    // afe_config->memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
    printf("afe config: %d\n", afe_config->afe_ringbuf_size);
    afe_config->afe_ringbuf_size = 50 * 10;
    // print/modify wake word model. 
    if (afe_config->wakenet_model_name) {
        printf("wakeword model in AFE config: %s\n", afe_config->wakenet_model_name);
    }
    if (afe_config->wakenet_model_name_2) {
            printf("wakeword model in AFE config: %s\n", afe_config->wakenet_model_name_2);
    }

    afe_handle = esp_afe_handle_from_config(afe_config);
    esp_afe_sr_data_t *afe_data = afe_handle->create_from_config(afe_config);
    
    // afe_handle->set_wakenet("wn9_xiaomeitongxue_tts");
    // 
    afe_config_free(afe_config);
    
    task_flag = 1;
    xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void*)afe_data, 5, NULL, 0);
    xTaskCreatePinnedToCore(&detect_Task, "detect", 4 * 1024, (void*)afe_data, 5, NULL, 1);
}

void app_main(void)
{
    // 初始i2s
    i2s_speaker_init();
    i2s_microphone_init();
    /*** 1. create esp tts handle  ***/
    tts_handle_init();
    // speek_chinese("测试tts初始化成功");
    /*** 2. init sr model ***/
    sr_init();

    
}
